﻿using Xejen.Communication.Configuration;
using Xejen.Communication.Endpoints;
using Xejen.Communication.Http.Abstract;
using Xejen.Communication.Http.Configuration;
using Xejen.Logger;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Text;
using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
using Xejen.Communication.Http;

namespace Xejen.Communication.Http.Communicator
{
    /// <summary>
    /// 基于http协议实现的通讯器
    /// </summary>
    /// <typeparam name="THttpInvoker"><inheritdoc cref="IHttpInvoker" path="/summary"/></typeparam>
    /// <creator>marc</creator>
    public class HttpCommunicator<THttpInvoker> : Communicator<HttpConfig, HeartbeatConfig, HttpAddress>, ICommunicator<HttpConfig, HeartbeatConfig>, IHttpCommunicator
        where THttpInvoker : IHttpInvoker
    {
        private delegate Task<HttpResponse> HttpOperationAsync<TInput>(string uri, object data, int timeout, CancellationToken cancellationToken);
        private delegate Task<HttpResponse> HttpOperationAsync(string uri, int timeout, CancellationToken cancellationToken);

        /// <inheritdoc cref="IHttpInvokerCreator{THttpInvoker}"/>
        protected IHttpInvokerCreator<THttpInvoker> HttpInvokerCreator { get; }

        /// <inheritdoc/>
        protected override JsonSerializerOptions JsonSerializerOptions => Config.JsonSerializerOptions;

        /// <inheritdoc cref="HttpCommunicator{THttpInvoker}"/>
        /// <param name="config"><inheritdoc cref="Communicator{TConfig, HeartbeatConfig, TAddress}.Config" path="/summary"/></param>
        /// <param name="httpInvokerCreator"><inheritdoc cref="HttpInvokerCreator" path="/summary"/></param>
        /// <param name="loggerManager"><inheritdoc cref="ILoggerManager" path="/summary"/></param>
        public HttpCommunicator(
            HttpConfig config,
            IHttpInvokerCreator<THttpInvoker> httpInvokerCreator,
            ILoggerManager loggerManager) : base(config, loggerManager)
        {
            Check.NotNull(config, nameof(config));
            Check.NotNull(httpInvokerCreator, nameof(httpInvokerCreator));
            Check.NotNull(loggerManager, nameof(loggerManager));

            Config = config;
            HttpInvokerCreator = httpInvokerCreator;
            Logger = loggerManager.CreateLogger<HttpCommunicator<THttpInvoker>>();
        }

        /// <inheritdoc/>
        public virtual async Task<HttpResponse> DeleteAsync(HttpAddress address, HttpConfig httpConfig = null, int timeout = -1, CancellationToken cancellationToken = default)
        {
            Check.NotNull(address, nameof(address));

            var config = httpConfig ?? Config;
            var task = HttpInvokerCreator.Create(config, config.Headers).DeleteAsync(address.RequestUri, GetTimeout(timeout), cancellationToken);

            return await LogInvokeAsync(task, address, timeout: GetTimeout(timeout), cancellationToken: cancellationToken);
        }
        /// <inheritdoc/>
        public virtual async Task<HttpResponse> GetAsync(HttpAddress address, HttpConfig httpConfig = null, int timeout = -1, CancellationToken cancellationToken = default)
        {
            Check.NotNull(address, nameof(address));

            var config = httpConfig ?? Config;
            var task = HttpInvokerCreator.Create(config, config.Headers).GetAsync(address.RequestUri, GetTimeout(timeout), cancellationToken);

            return await LogInvokeAsync(task, address, timeout: GetTimeout(timeout), cancellationToken: cancellationToken);
        }
        /// <inheritdoc/>
        public virtual async Task<HttpResponse> PostAsync<TInput>(HttpAddress address, TInput input, HttpConfig httpConfig = null, int timeout = -1, CancellationToken cancellationToken = default)
        {
            Check.NotNull(address, nameof(address));

            var config = httpConfig ?? Config;
            var task = HttpInvokerCreator.Create(config, config.Headers).PostAsync(address.RequestUri, input, GetTimeout(timeout), cancellationToken);

            return await LogInvokeAsync(task, address, timeout: GetTimeout(timeout), input: input, cancellationToken: cancellationToken);
        }
        /// <inheritdoc/>
        public virtual async Task<HttpResponse> PutAsync<TInput>(HttpAddress address, TInput input, HttpConfig httpConfig = null, int timeout = -1, CancellationToken cancellationToken = default)
        {
            Check.NotNull(address, nameof(address));

            var config = httpConfig ?? Config;
            var task = HttpInvokerCreator.Create(config, config.Headers).PutAsync(address.RequestUri, input, GetTimeout(timeout), cancellationToken);

            return await LogInvokeAsync(task, address, timeout: GetTimeout(timeout), input: input, cancellationToken: cancellationToken);
        }
    }
}
